/*
 LXTimeProtocol :倒计时的协议,需要倒计时的界面,实现这个协议
 -timeFire: 正在倒计时,一秒钟调用一次
 --leftInterval:倒计时剩余时间
 -timeEnd: 倒计时结束
 
 **/

import UIKit
//LXTimeProtocol :倒计时的协议,需要倒计时的界面,实现这个协议
public protocol LXTimeProtocol :NSObjectProtocol {
    //用来控制某个object需要倒计时但是不需要刷新UI的时候,比如push到下一个界面,当前界面没销毁但是不需要刷新
    var isNeedFire : Bool { get } //作为实例使用时可读写,转换成协议使用时,只读
    //正在倒计时,一秒钟调用一次
    func timeFire(leftInterval : Int)
    //倒计时结束,timeEnd不受isNeedFire影响,避免需要处理过多逻辑
    func timeEnd()
}
public extension LXTimeProtocol {
    var isNeedFire : Bool {
        return true //默认需要调用timeFire
    }

}
public class XTimeManager: NSObject {
    /*将XTimeObject放到单例数组管理,隔一段时间清理一次倒计时结束或者弱引用已经销毁的XTimeObject实例
     问题:1.manager数量多了,同时在主线程执行timeFire操作,会不会卡顿甚至崩溃
         2.执行timeFire时需要遍历集合,进行读写操作,addTarget时也需要进行读写操作,如何保证线程安全
         3.能否识别
     */
    public static let shared = XTimeManager.init()
    public var manager : Set<XTimeObject> = []
    public var codeTimer = DispatchSource.makeTimerSource(queue:DispatchQueue.global())
    //定时器状态 0-停止,1-运行中
    public var timerState : Int = 0


    public override init() {
        super.init()
    }
    
    public func prepareCodeTimer(){
        if self.codeTimer.isCancelled {
            self.codeTimer = DispatchSource.makeTimerSource(queue:DispatchQueue.global())
        }
        codeTimer.schedule(deadline: .now(), repeating: .seconds(1))
    
        // 设定时间源的触发事件
        codeTimer.setEventHandler(handler: {
            LXLog("[XTimeManager]定时器running")
            // 返回主线程处理一些事件，更新UI等等
            DispatchQueue.main.async {
                //@warning 这里是否会卡顿或者线程不安全
                let managerList = self.manager
                for timeObject in managerList {
                    if timeObject.object == nil {
                        self.manager.remove(timeObject)
                    }else{
                        let target = timeObject.object!
                        timeObject.leftInterval = Int(timeObject.endDate?.timeIntervalSinceNow ?? 0)

                        if timeObject.leftInterval <= 0 {
                            target.timeEnd()
                            self.manager.remove(timeObject)
                        }
                        else if target.isNeedFire {
                            target.timeFire(leftInterval: timeObject.leftInterval)
                        }
                    }
                }
                //如果没有需要倒计时的序列了,就取消倒计时,后面addTarget的时候重新打开
                if self.manager.count == 0 {
                    self.codeTimer.cancel()
                    self.timerState = 0
                }
            }
            
        })
        // 启动时间源
        self.codeTimer.resume()
        self.timerState = 1
    }
    
    public class func addTarget(target:LXTimeProtocol!, timeInterval:Int) {
        if timeInterval > 0 {
            //@warning这里的遍历是否需要指定主线程(和倒计时里的遍历保持一致),或者其它方式避免线程冲突
            let managerList = self.shared.manager
            for timeObject in managerList {
                /*
                 如果一个实例已经添加过倒计时了,要先移除旧的
                 想要一个界面放多个倒计时,可以把倒计时放在不同子界面,
                 或者设置timeInterval为多个倒计时时间的最大值,自己在timeFire里实现每个倒计时的计数逻辑
                 */
                if timeObject.object?.isEqual(target) ?? false {
                    self.shared.manager.remove(timeObject)
                }
            }
            
            let obj = XTimeObject.init(obj: target, leftTime: timeInterval)
            self.shared.manager.insert(obj)
            
//            _ = NSValue.init(nonretainedObject: target)
        }
        if self.shared.timerState == 0 {
            self.shared.prepareCodeTimer()
        }

    }
    
    public class func hasTarget(target:LXTimeProtocol!) -> Bool {
        let managerList = self.shared.manager
        for timeObject in managerList {
            if timeObject.object?.isEqual(target) ?? false {
                return true
            }
        }
        return false
    }

    
    public class func removeTarget(target:LXTimeProtocol!) {
        let managerList = self.shared.manager
        for timeObject in managerList {
            if timeObject.object?.isEqual(target) ?? false {
                self.shared.manager.remove(timeObject)
            }
        }
    }

    
}

public class XTimeObject: NSObject {
    //弱引用实现该协议的类
    public weak var object : LXTimeProtocol?
    
    public var endDate : Date?
    public var  timeInterval : Int
    public var  leftInterval : Int
    
    public init(obj:LXTimeProtocol?, leftTime: Int) {
        self.object = obj
        self.timeInterval = leftTime
        self.endDate = Date.init(timeIntervalSinceNow: TimeInterval(self.timeInterval))
        self.leftInterval = leftTime
        super.init()
    }
    
}
